<?php

namespace cf;

require_once dirname(__FILE__).'/config.php';
require_once Config::path.'cookie.php';
require_once Config::path.'db.php';

class User
{
	const ERR_INVALID_USER = 1;
	const ERR_PASSWORD_INCORRECT = 2;
	
	function __construct($id)
	{
		$user = query2array("
			SELECT login, password, salt, name, email, descr, registered, last_login, status, image
			FROM cf_users
			WHERE id=:id",
			array('id'=>$id)
		);
		if (empty($user)) {
			throw new \Exception('Invalid user');
		}
		$this->id = (int)$id;
		$this->login = $user['login'];
		$this->password = $user['password'];
		$this->salt = $user['salt'];
		$this->name = $user['name'];
		$this->email = $user['email'];
		$this->descr = $user['descr'];
		$this->registered = $user['registered'];
		$this->last_login = $user['last_login'];
		$this->status = $user['status'];
		$this->image = $user['image'];
		
		$this->roles = query2arrays('
			SELECT cf_roles.id AS id, cf_roles.name, cf_roles.descr
			FROM cf_roles
			INNER JOIN cf_user_roles ON cf_roles.id = cf_user_roles.role_id
			WHERE cf_user_roles.user_id=:uid
			ORDER BY name',
			array('uid'=>$this->id), 
			false, 
			'id'
		);
	}
	
	public function name()
	{
		return $this->name;
	}

	public function loginName()
	{
		return $this->login;
	}
	
	public function id()
	{
		return $this->id;
	}

	public function email()
	{
		return $this->email;
	}
	
	public function image()
	{
		return $this->image;
	}
	
	public function salt()
	{
		return $this->salt;
	}
	
	public function status()
	{
		return $this->status;
	}
	
	public function is_a($roleId)
	{
		return array_key_exists($roleId,$this->roles);
	}
	
	public function logout()
	{
		$c = Cookie::retrieve('cyberfish_login');
		if ($c) {
			$c->del();
		}
	}
	
	
	/**	@return user ID	or false */
	static public function findByLogin($login)
	{
		return query2var("SELECT id FROM cf_users WHERE login=:login", array('login'=>$login));
	}
	
	/**	@return user ID	or false */
	static public function findByEmail($email)
	{
		$q = createQuery(
			"SELECT id FROM cf_users WHERE email=:email",
			array('email'=>trim($email))
		);
		$q->execute();
		if (!$q->fetch())
		{
			return false;
		}
		return $q->at('id');
	}
	
	/** @return  ID of new user	*/
	static public function register($login, $pass, $name, $status=0, $email='', $descr='', $roles=array())
	{
		if (self::findByLogin($login))
		{
			throw new \Exception("User with login '$login' already exists");
		}
		
		$salt = substr(md5(microtime().rand()), 0, 8);
		
		$uid = execQuery('
			INSERT INTO cf_users (login,password,salt,name,status,email,descr,registered)
			VALUES (:login,:password,:salt,:name,:status,:email,:descr,NOW())',
			array(
				'login'=>$login, 
				'password'=>self::generatePassword($pass,$salt), 
				'salt'=>$salt, 
				'name'=>$name, 
				'status'=>$status ? 1 : 0,
				'email'=>$email,
				'descr'=>$descr
			)
		);
		
		if ($roles) {
			self::setRoles($uid, $roles);
		}
		
		return $uid;
	}
	
	static public function setPassword($id, $pass)
	{
		execQuery(
			'UPDATE cf_users SET password=:pass WHERE id=:id', 
			array('id'=>$id, 'pass'=>self::generatePassword(trim($pass),query2var('SELECT salt FROM cf_users WHERE id=:id',array('id'=>$id))))
		);
	}
	
	static public function setRoles($uid, $roleId=array())
	{
		execQuery('DELETE FROM cf_user_roles WHERE user_id=:uid',array('uid'=>$uid));
		$q = createQuery('INSERT INTO cf_user_roles (user_id,role_id) VALUES(:uid,:rid)');
		$roleId = is_array($roleId) ? $roleId : array($roleId);
		foreach ($roleId as $rid) {
			$q->setParam('uid',$uid);
			$q->setParam('rid',$rid);
			$q->execute();
		}
	}
	
	static public function login($login, $pass, $remember=false)
	{
		$login = trim($login);
		$id = self::findByLogin($login);
		if (!$id) {
			throw new \Exception("Invalid username",self::ERR_INVALID_USER);
		}
		
		$user = new User($id);
		if (!$user->checkPassword($pass)) {
			throw new \Exception("The password for the username $login is incorrect", self::ERR_PASSWORD_INCORRECT);
		}
	
		self::setAuthCookie($user, $remember);
		
		self::updateLastLoginTime($user->id());
		
		return $user;
	}
	
	static public function getLoggedIn()
	{
		$c = Cookie::retrieve('cyberfish_login');
		if (!$c) {
			return false;
		}
		
		list($login, $loginExpireAt, $hash) = explode('|',$c->getParam('id'));
		
		if (time() > $loginExpireAt) {
			return false;
		}
		
		$uid = self::findByLogin($login);
		if (!$uid) {
			return false;
		}
		$user = new User($uid);
		
		$key = hash_hmac('md5', $user->loginName() . '|' . $loginExpireAt, $user->salt());
		if ($hash != hash_hmac('md5', $user->loginName() . '|' . $loginExpireAt, $key)) {
			return false;
		}

		self::updateLastLoginTime($uid);
		return $user;
	}
	
	/**
		find user by email and sets a random password
	*/
	static public function resetPwdByEmail($email)
	{
		$uid = self::findByEmail($email);
		if (!$uid) {
			return false;
		}
		$pwd = uniqid(rand());
		self::setPassword($uid, $pwd);
		return array(new User($uid), $pwd);
	}

	static private function setAuthCookie($user, $remember)
	{
		$loginExpireAt = time() + 2 * 24 * 60 * 60;	//2 days
		$cookieExpireMin = 0; //don't remember
		if ($remember) {
			$loginExpireAt = time() + 14 * 24 * 60 * 60; //2 weeks
			$cookieExpireMin = 14 * 24 * 60; //remember for 2 weeks
		}
		
		$key = hash_hmac('md5', $user->loginName() . '|' . $loginExpireAt, $user->salt());
		$hash = hash_hmac('md5', $user->loginName() . '|' . $loginExpireAt, $key);
		$cookie = $user->loginName() . '|' . $loginExpireAt . '|' . $hash;

		$c = new Cookie('cyberfish_login', $cookieExpireMin);
		$c->setParam('id', $cookie);
		$c->set();
	}
	
	static private function updateLastLoginTime($id)
	{
		execQuery("UPDATE cf_users SET last_login=NOW() WHERE id=$id");
	}	
	
	private function checkPassword($pass)
	{
		return self::generatePassword($pass,$this->salt) == $this->password;
	}
	
	static private function generatePassword($pass, $salt)
	{
		return md5($pass.$salt);
	}
	
	private $login = null;
	private $password = null;
	private $salt = null;
	private $name = null;
	private $email = null;
	private $descr = null;
	private $registered = null;
	private $last_login = null;
	private $enabled = null;
	private $image = null;
	private $roles = array();
};


?>